{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "beObUOFyuRjT"
      },
      "source": [
        "##### Copyright 2018 The TF-Agents Authors."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "nQnmcm0oI1Q-"
      },
      "outputs": [],
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "eutDVTs9aJEL"
      },
      "source": [
        "# 재현 버퍼\n",
        "\n",
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td><a target=\"_blank\" href=\"https://www.tensorflow.org/agents/tutorials/5_replay_buffers_tutorial\"><img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\">TensorFlow.org에서 보기</a></td>\n",
        "  <td><a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs-l10n/blob/master/site/ko/agents/tutorials/5_replay_buffers_tutorial.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\">Google Colab에서 실행하기</a></td>\n",
        "  <td><a target=\"_blank\" href=\"https://github.com/tensorflow/docs-l10n/blob/master/site/ko/agents/tutorials/5_replay_buffers_tutorial.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\">GitHub에서 소스 보기</a></td>\n",
        "  <td><a href=\"https://storage.googleapis.com/tensorflow_docs/docs-l10n/site/ko/agents/tutorials/5_replay_buffers_tutorial.ipynb\"><img src=\"https://www.tensorflow.org/images/download_logo_32px.png\">노트북 다운로드하기</a></td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "8aPHF9kXFggA"
      },
      "source": [
        "## 소개\n",
        "\n",
        "강화 학습 알고리즘은 재현 버퍼를 사용하여 환경에서 정책을 실행할 때 경험의 궤적을 저장합니다. 훈련 중에, 에이전트의 경험을 \"재현\"하기 위해 궤적의 서브 세트(순차 서브 세트 또는 샘플)에 대해 재현 버퍼가 조회됩니다.\n",
        "\n",
        "이 colab에서는 일반적인 API를 공유하는 python-backed 및 tensorflow-backed의 두 가지 유형의 재현 버퍼를 탐색합니다. 다음 섹션에서는 API, 각 버퍼 구현 및 데이터 수집 훈련 중에 API와 버퍼 구현을 사용하는 방법에 관해 설명합니다.\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "1uSlqYgvaG9b"
      },
      "source": [
        "## 설정"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "GztmUpWKZ7kq"
      },
      "source": [
        "아직 설치하지 않았다면, tf-agents를 설치합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "TnE2CgilrngG"
      },
      "outputs": [],
      "source": [
        "!pip install tf-agents\n",
        "!pip install gym"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "whYNP894FSkA"
      },
      "outputs": [],
      "source": [
        "from __future__ import absolute_import\n",
        "from __future__ import division\n",
        "from __future__ import print_function\n",
        "\n",
        "import tensorflow as tf\n",
        "import numpy as np\n",
        "\n",
        "from tf_agents import specs\n",
        "from tf_agents.agents.dqn import dqn_agent\n",
        "from tf_agents.drivers import dynamic_step_driver\n",
        "from tf_agents.environments import suite_gym\n",
        "from tf_agents.environments import tf_py_environment\n",
        "from tf_agents.networks import q_network\n",
        "from tf_agents.replay_buffers import py_uniform_replay_buffer\n",
        "from tf_agents.replay_buffers import tf_uniform_replay_buffer\n",
        "from tf_agents.specs import tensor_spec\n",
        "from tf_agents.trajectories import time_step\n",
        "\n",
        "tf.compat.v1.enable_v2_behavior()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "xcQWclL9FpZl"
      },
      "source": [
        "## 재현 버퍼 API\n",
        "\n",
        "재현 버퍼 클래스에는 다음과 같은 정의 및 메서드가 있습니다.\n",
        "\n",
        "```python\n",
        "class ReplayBuffer(tf.Module):   \"\"\"Abstract base class for TF-Agents replay buffer.\"\"\"    def __init__(self, data_spec, capacity):     \"\"\"Initializes the replay buffer.      Args:       data_spec: A spec or a list/tuple/nest of specs describing         a single item that can be stored in this buffer       capacity: number of elements that the replay buffer can hold.     \"\"\"    @property   def data_spec(self):     \"\"\"Returns the spec for items in the replay buffer.\"\"\"    @property   def capacity(self):     \"\"\"Returns the capacity of the replay buffer.\"\"\"    def add_batch(self, items):     \"\"\"Adds a batch of items to the replay buffer.\"\"\"    def get_next(self,                sample_batch_size=None,                num_steps=None,                time_stacked=True):     \"\"\"Returns an item or batch of items from the buffer.\"\"\"    def as_dataset(self,                  sample_batch_size=None,                  num_steps=None,                  num_parallel_calls=None):     \"\"\"Creates and returns a dataset that returns entries from the buffer.\"\"\"     def gather_all(self):     \"\"\"Returns all the items in buffer.\"\"\"     return self._gather_all()    def clear(self):     \"\"\"Resets the contents of replay buffer\"\"\"\n",
        "```\n",
        "\n",
        "재현 버퍼 객체가 초기화될 때 저장할 요소의 `data_spec` 이 필요합니다. 이 사양은 버퍼에 추가될 궤적 요소의 `TensorSpec`에 해당합니다. 이 사양은 일반적으로 훈련 시 에이전트가 기대하는 형상, 유형 및 구조를 정의하는 에이전트의 `agent.collect_data_spec`를 통해 획득됩니다(나중에 자세히 설명)."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "X3Yrxg36Ik1x"
      },
      "source": [
        "## TFUniformReplayBuffer\n",
        "\n",
        "`TFUniformReplayBuffer`는 TF-Agents에서 가장 일반적으로 사용되는 재현 버퍼이므로 이 튜토리얼에서 사용합니다. `TFUniformReplayBuffer`에서 버퍼 스토리지의 지원은 tensorflow 변수에 의해 수행되므로 계산 그래프의 일부입니다.\n",
        "\n",
        "버퍼는 요소의 배치를 저장하며 배치 세그먼트당 최대 용량 `max_length` 요소를 갖습니다. 따라서 총 버퍼 용량은 `batch_size` x `max_length` 요소입니다. 버퍼에 저장된 요소는 모두 일치하는 데이터 사양을 가져야 합니다. 재현 버퍼가 데이터 수집에 사용되는 경우, 사양은 에이전트의 수집 데이터 사양입니다.\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "lYk-bn2taXlw"
      },
      "source": [
        "### 버퍼 만들기:\n",
        "\n",
        "`TFUniformReplayBuffer`를 만들려면 다음을 전달합니다.\n",
        "\n",
        "1. 버퍼가 저장할 데이터 요소의 사양\n",
        "2. 버퍼의 배치 크기에 해당하는 `batch size`\n",
        "3. 배치 세그먼트당 `max_length` 개수의 요소\n",
        "\n",
        "다음은 샘플 데이터 사양, `batch_size` 32 및`max_length` 1000을 가진 `TFUniformReplayBuffer`를 생성하는 예제입니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Dj4_-77_5ExP"
      },
      "outputs": [],
      "source": [
        "data_spec =  (\n",
        "        tf.TensorSpec([3], tf.float32, 'action'),\n",
        "        (\n",
        "            tf.TensorSpec([5], tf.float32, 'lidar'),\n",
        "            tf.TensorSpec([3, 2], tf.float32, 'camera')\n",
        "        )\n",
        ")\n",
        "\n",
        "batch_size = 32\n",
        "max_length = 1000\n",
        "\n",
        "replay_buffer = tf_uniform_replay_buffer.TFUniformReplayBuffer(\n",
        "    data_spec,\n",
        "    batch_size=batch_size,\n",
        "    max_length=max_length)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "XB8rOw5ATDD2"
      },
      "source": [
        "### 버퍼에 쓰기:\n",
        "\n",
        "재현 버퍼에 요소를 추가하기 위해 `add_batch(items)` 메서드를 사용합니다. 여기서 `items`는 버퍼에 추가할 항목의 배치를 나타내는 텐서의 목록/튜플/중첩입니다. `items`의 각 요소는 `batch_size`와 동일한 외부 차원을 가져야 하고 나머지 차원은 항목의 데이터 사양을 준수해야 합니다(재현 버퍼 생성자에 전달된 데이터 사양과 같음).\n",
        "\n",
        "다음은 항목의 배치를 추가하는 예제입니다.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "nOvkp4vJhBOT"
      },
      "outputs": [],
      "source": [
        "action = tf.constant(1 * np.ones(\n",
        "    data_spec[0].shape.as_list(), dtype=np.float32))\n",
        "lidar = tf.constant(\n",
        "    2 * np.ones(data_spec[1][0].shape.as_list(), dtype=np.float32))\n",
        "camera = tf.constant(\n",
        "    3 * np.ones(data_spec[1][1].shape.as_list(), dtype=np.float32))\n",
        "  \n",
        "values = (action, (lidar, camera))\n",
        "values_batched = tf.nest.map_structure(lambda t: tf.stack([t] * batch_size),\n",
        "                                       values)\n",
        "  \n",
        "replay_buffer.add_batch(values_batched)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "smnVAxHghKly"
      },
      "source": [
        "### 버퍼에서 읽기\n",
        "\n",
        "`TFUniformReplayBuffer`에서 데이터를 읽는 방법에는 3가지가 있습니다.\n",
        "\n",
        "1. `get_next()` - 버퍼에서 하나의 샘플을 반환합니다. 반환된 샘플 배치 크기 및 타임스텝 수는 이 메서드에 대한 인수를 통해 지정할 수 있습니다.\n",
        "2. `as_dataset()` - 재현 버퍼를 `tf.data.Dataset`로 반환합니다. 그런 다음, 데이터세트 반복기를 만들고 버퍼에 있는 항목의 샘플을 반복할 수 있습니다.\n",
        "3. `gather_all()` - 버퍼에 있는 모든 항목을 형상 `[batch, time, data_spec]`을 가진 Tensor로 반환합니다\n",
        "\n",
        "다음은 이들 각 메서드를 사용하여 재현 버퍼에서 데이터를 읽는 방법의 예제입니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "IlQ1eGhohM3M"
      },
      "outputs": [],
      "source": [
        "# add more items to the buffer before reading\n",
        "for _ in range(5):\n",
        "  replay_buffer.add_batch(values_batched)\n",
        "\n",
        "# Get one sample from the replay buffer with batch size 10 and 1 timestep:\n",
        "\n",
        "sample = replay_buffer.get_next(sample_batch_size=10, num_steps=1)\n",
        "\n",
        "# Convert the replay buffer to a tf.data.Dataset and iterate through it\n",
        "dataset = replay_buffer.as_dataset(\n",
        "    sample_batch_size=4,\n",
        "    num_steps=2)\n",
        "\n",
        "iterator = iter(dataset)\n",
        "print(\"Iterator trajectories:\")\n",
        "trajectories = []\n",
        "for _ in range(3):\n",
        "  t, _ = next(iterator)\n",
        "  trajectories.append(t)\n",
        "  \n",
        "print(tf.nest.map_structure(lambda t: t.shape, trajectories))\n",
        "\n",
        "# Read all elements in the replay buffer:\n",
        "trajectories = replay_buffer.gather_all()\n",
        "\n",
        "print(\"Trajectories from gather all:\")\n",
        "print(tf.nest.map_structure(lambda t: t.shape, trajectories))\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "BcS49HrNF34W"
      },
      "source": [
        "## PyUniformReplayBuffer\n",
        "\n",
        "`PyUniformReplayBuffer`는 `TFUniformReplayBuffer`와 기능은 같지만, tf 변수 대신 데이터가 numpy 배열에 저장됩니다. 이 버퍼는 그래프를 벗어난 데이터(out-of-graph data) 수집에 사용될 수 있습니다. 백업 스토리지를 numpy에 저장하면 일부 애플리케이션에서 Tensorflow 변수를 사용하지 않고 데이터 조작(예: 우선 순위 업데이트를 위한 인덱싱)을 보다 쉽게 ​​수행할 수 있습니다. 그러나 이 구현에는 Tensorflow를 사용한 그래프 최적화의 이점이 없습니다.\n",
        "\n",
        "다음은 에이전트의 정책 궤적 사양에서 `PyUniformReplayBuffer`를 인스턴스화하는 예제입니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "F4neLPpL25wI"
      },
      "outputs": [],
      "source": [
        "replay_buffer_capacity = 1000*32 # same capacity as the TFUniformReplayBuffer\n",
        "\n",
        "py_replay_buffer = py_uniform_replay_buffer.PyUniformReplayBuffer(\n",
        "    capacity=replay_buffer_capacity,\n",
        "    data_spec=tensor_spec.to_nest_array_spec(data_spec))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "9V7DEcB8IeiQ"
      },
      "source": [
        "## 훈련 중 재현 버퍼 사용하기\n",
        "\n",
        "이제 재현 버퍼를 작성하고, 재현 버퍼에/에서 항목을 쓰고 읽는 방법을 알았으므로 에이전트를 훈련하는 동안 궤적을 저장하는 데 사용할 수 있습니다.\n",
        "\n",
        "### 데이터 수집\n",
        "\n",
        "먼저 데이터 수집 중에 재현 버퍼를 사용하는 방법을 살펴보겠습니다.\n",
        "\n",
        "TF-Agents에서는 환경에서 경험을 수집하기 위해 `Driver`(자세한 내용은 드라이버 튜토리얼 참조)를 사용합니다. `Driver`를 사용하려면, `Driver`가 궤적을 받을 때 실행하는 함수인 `Observer`를 지정합니다.\n",
        "\n",
        "따라서 재현 버퍼에 궤적 요소를 추가하기 위해 `add_batch(items)`를 호출하여 재현 버퍼에 항목(의 배치)을 추가하는 observer를 추가합니다.\n",
        "\n",
        "아래는 `TFUniformReplayBuffer`를 사용한 예제입니다. 먼저 환경, 네트워크 및 에이전트를 만듭니다. 그런 다음, `TFUniformReplayBuffer`를 만듭니다. 재현 버퍼에 있는 궤적 요소의 사양은 에이전트의 수집 데이터 사양과 동일합니다. 그런 다음, `add_batch` 메서드를 훈련 중에 데이터 수집을 수행하는 드라이버의 observer로 설정합니다.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "pCbTDO3Z5UCS"
      },
      "outputs": [],
      "source": [
        "env = suite_gym.load('CartPole-v0')\n",
        "tf_env = tf_py_environment.TFPyEnvironment(env)\n",
        "\n",
        "q_net = q_network.QNetwork(\n",
        "    tf_env.time_step_spec().observation,\n",
        "    tf_env.action_spec(),\n",
        "    fc_layer_params=(100,))\n",
        "\n",
        "agent = dqn_agent.DqnAgent(\n",
        "    tf_env.time_step_spec(),\n",
        "    tf_env.action_spec(),\n",
        "    q_network=q_net,\n",
        "    optimizer=tf.compat.v1.train.AdamOptimizer(0.001))\n",
        "\n",
        "replay_buffer_capacity = 1000\n",
        "\n",
        "replay_buffer = tf_uniform_replay_buffer.TFUniformReplayBuffer(\n",
        "    agent.collect_data_spec,\n",
        "    batch_size=tf_env.batch_size,\n",
        "    max_length=replay_buffer_capacity)\n",
        "\n",
        "# Add an observer that adds to the replay buffer:\n",
        "replay_observer = [replay_buffer.add_batch]\n",
        "\n",
        "collect_steps_per_iteration = 10\n",
        "collect_op = dynamic_step_driver.DynamicStepDriver(\n",
        "  tf_env,\n",
        "  agent.collect_policy,\n",
        "  observers=replay_observer,\n",
        "  num_steps=collect_steps_per_iteration).run()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "huGCDbO4GAF1"
      },
      "source": [
        "### train step에 대한 데이터 읽기\n",
        "\n",
        "재현 버퍼에 궤적 요소를 추가한 후 재현 버퍼에서 궤적의 배치를 읽어 train step의 입력 데이터로 사용할 수 있습니다.\n",
        "\n",
        "다음은 훈련 루프에서 재현 버퍼로부터 궤적에 대해 훈련하는 방법의 예제입니다. "
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "gg8SUyXXnSMr"
      },
      "outputs": [],
      "source": [
        "# Read the replay buffer as a Dataset,\n",
        "# read batches of 4 elements, each with 2 timesteps:\n",
        "dataset = replay_buffer.as_dataset(\n",
        "    sample_batch_size=4,\n",
        "    num_steps=2)\n",
        "\n",
        "iterator = iter(dataset)\n",
        "\n",
        "num_train_steps = 10\n",
        "\n",
        "for _ in range(num_train_steps):\n",
        "  trajectories, _ = next(iterator)\n",
        "  loss = agent.train(experience=trajectories)\n"
      ]
    }
  ],
  "metadata": {
    "colab": {
      "collapsed_sections": [],
      "name": "5_replay_buffers_tutorial.ipynb",
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
